package jp.dodododo.aop.instrument;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.CtPrimitiveType;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationDefaultAttribute;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.AttributeInfo;
import javassist.bytecode.CodeIterator;
import javassist.bytecode.MethodInfo;

import javax.interceptor.ExcludeClassInterceptors;

import jp.dodododo.aop.Aspect;
import jp.dodododo.aop.Aspects;
import jp.dodododo.aop.Def;
import jp.dodododo.aop.annotation.AppendFirst;
import jp.dodododo.aop.annotation.AppendLast;
import jp.dodododo.aop.annotation.Enhance;
import jp.dodododo.aop.config.AspectConfigBuilder;
import jp.dodododo.aop.config.AspectConfigs;
import jp.dodododo.aop.util.ClassPoolUtil;

public class Transformer implements ClassFileTransformer {
	private static final String DEBUG_KEY = "samurai.aop.debug";

	private static final boolean DEBUG = Boolean.getBoolean(DEBUG_KEY);

	public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
			ProtectionDomain protectionDomain, byte[] classfileBuffer) {

		try {
			ClassPool classPool = ClassPoolUtil.getClassPool(loader);

			CtClass ctClass = null;
			if (className.endsWith("Test")) {
				ctClass = makeUnFrozenClass(classPool, classfileBuffer);
				appnendAspectConfig(loader, ctClass);
			}
			Def aspectDef = AspectConfigs.getAspectDef(loader, className);
			if (aspectDef == null || aspectDef.getAspects().size() == 0) {
				return null;
			}
			if (ctClass == null) {
				ctClass = makeUnFrozenClass(classPool, classfileBuffer);
			}
			if (ctClass == null) {
				return null;
			}
			return transform(loader, aspectDef, ctClass);
		} catch (Throwable ex) {
			// IllegalClassFormatException e = new
			// IllegalClassFormatException();
			// e.initCause(ex);
			// throw e;
			System.out.println("ERROR : " + className + "[" + ex + "]");
			ex.printStackTrace();
			return null;
		} finally {
		}
	}

	private void appnendAspectConfig(ClassLoader loader, CtClass ctClass) throws ClassNotFoundException {
		if (ctClass == null) {
			return;
		}
		Aspects aspects = AspectConfigs.getAspects(loader);
		if (aspects == null) {
			return;
		}
		Object[] annotations = ctClass.getAnnotations();
		for (int i = 0; i < annotations.length; i++) {
			Object annotation = annotations[i];
			if (annotation instanceof AppendFirst) {
				String path = ((AppendFirst) annotation).value();
				Aspects newAspects = new AspectConfigBuilder().build(path);
				aspects.appendFirst(newAspects);
			} else if (annotation instanceof AppendLast) {
				String path = ((AppendLast) annotation).value();
				Aspects newAspects = new AspectConfigBuilder().build(path);
				aspects.appendLast(newAspects);
			} else if (annotation.toString().startsWith("@" + AppendFirst.class.getName() + "(")) {
				String path = getConfigPath(annotation);
				Aspects newAspects = new AspectConfigBuilder().build(path);
				aspects.appendFirst(newAspects);
			} else if (annotation.toString().startsWith("@" + AppendLast.class.getName() + "(")) {
				String path = getConfigPath(annotation);
				Aspects newAspects = new AspectConfigBuilder().build(path);
				aspects.appendLast(newAspects);
			}
		}
	}

	private String getConfigPath(Object annotation) {
		return annotation.toString().split("\"")[1];
	}

	private CtClass makeUnFrozenClass(ClassPool classPool, byte[] classfileBuffer) throws IOException {
		CtClass ctClass;
		ByteArrayInputStream stream = null;
		try {
			stream = new ByteArrayInputStream(classfileBuffer);
			ctClass = classPool.makeClass(stream);
		} catch (RuntimeException frozen) {
			return null;
		} finally {
			try {
				stream.close();
			} catch (Exception ignore) {
			}
		}
		if (ctClass.isFrozen() == true) {
			return null;
		}
		if (ctClass.isInterface() == true) {
			return null;
		}
		if (ctClass.isArray() == true) {
			return null;
		}
		if (ctClass.isAnnotation() == true) {
			return null;
		}
		return ctClass;
	}

	private byte[] transform(ClassLoader loader, Def aspectDef, CtClass ctClass) throws Exception {
		List<Aspect> aspects = aspectDef.getAspects();
		List<CtMethod> targetMethods = getTargetMethods(ctClass);
		for (Iterator<CtMethod> methods = targetMethods.iterator(); methods.hasNext();) {
			CtMethod method = methods.next();

			if (isEnhance(method) == false) {
				continue;
			}

			for (Iterator<Aspect> iter = aspects.iterator(); iter.hasNext();) {
				Aspect aspect = iter.next();
				if (aspect.isApplied(method) == true) {
					String[] interceptorStatements = aspectDef.getApplyInterceptorStatements(method);
					if (0 < interceptorStatements.length) {
						weaveMethod(ctClass, method, interceptorStatements, aspect.isDefaultPointcut());
					}
					break;
				}
			}
		}
		List<CtConstructor> targetConstructors = getTargetConstructors(ctClass);
		for (Iterator<CtConstructor> constructors = targetConstructors.iterator(); constructors.hasNext();) {
			CtConstructor constructor = constructors.next();

			if (isEnhance(constructor) == false) {
				continue;
			}

			for (Iterator<Aspect> iter = aspects.iterator(); iter.hasNext();) {
				Aspect aspect = iter.next();
				if (aspect.isApplied(constructor) == true) {
					String[] interceptorStatements = aspectDef.getApplyInterceptorStatements(constructor);
					if (0 < interceptorStatements.length) {
						weaveConstructor(ctClass, constructor, interceptorStatements, aspect.isDefaultPointcut());
					}
					break;
				}
			}
		}
		return ctClass.toBytecode();
	}

	private void weaveConstructor(CtClass ctClass, CtConstructor constructor, String[] interceptorStatements,
			boolean isDefaultPointcut) throws Exception {
		String newMethodName = getDelegateMethodPrefix(ctClass) + "_this";
		CtMethod method = constructor.toMethod(newMethodName, ctClass);
		if (isEmpty(method) == true) {
			return;
		}
		ctClass.addMethod(method);

		int modifiers = constructor.getModifiers();
		String targetClassName = ctClass.getName();
		StringBuilder body = createBody(interceptorStatements, constructor.getName(), constructor, modifiers, newMethodName,
				targetClassName, true, isDefaultPointcut, ctClass);
		body.append("if(true){return;}");
		constructor.insertBeforeBody(body.toString());
	}

	private boolean isEmpty(CtMethod method) {
		if (method.isEmpty() == true) {
			return true;
		}
		MethodInfo methodInfo = method.getMethodInfo();
		CodeIterator iterator = methodInfo.getCodeAttribute().iterator();
		if (iterator.getCodeLength() == 0) {
			return true;
		}
		return false;
	}

	private List<CtMethod> getTargetMethods(CtClass ctClass) {
		List<CtMethod> ret = new ArrayList<CtMethod>();
		CtMethod[] declaredMethods = ctClass.getDeclaredMethods();
		for (int i = 0; i < declaredMethods.length; i++) {
			CtMethod method = declaredMethods[i];
			ret.add(method);
		}
		CtMethod[] methods = ctClass.getMethods();
		for (int i = 0; i < methods.length; i++) {
			CtMethod method = methods[i];
			if (ret.contains(method) == false && Modifier.isFinal(method.getModifiers()) == false) {
				ret.add(method);
			}
		}

		return ret;
	}

	private List<CtConstructor> getTargetConstructors(CtClass ctClass) {
		List<CtConstructor> ret = new ArrayList<CtConstructor>();
		CtConstructor[] declaredConstructors = ctClass.getDeclaredConstructors();
		for (int i = 0; i < declaredConstructors.length; i++) {
			CtConstructor constructor = declaredConstructors[i];
			ret.add(constructor);
		}
		CtConstructor[] constructors = ctClass.getConstructors();
		for (int i = 0; i < constructors.length; i++) {
			CtConstructor constructor = constructors[i];
			if (ret.contains(constructor) == false) {
				ret.add(constructor);
			}
		}
		return ret;
	}

	private boolean isEnhance(CtBehavior methodOrConstructor) throws ClassNotFoundException, NotFoundException {
		Object[] annotations = methodOrConstructor.getAvailableAnnotations();
		for (int i = 0; i < annotations.length; i++) {
			Object object = annotations[i];
			if (object instanceof Enhance) {
				Enhance enhance = (Enhance) object;
				return enhance.value();
			} else if (isExcludeClassInterceptors(object)) {
				return false;
			}
		}
		return isEnhance(methodOrConstructor.getDeclaringClass());
	}

	private boolean isExcludeClassInterceptors(Object object) {
		if (object == null) {
			return false;
		}
		if (object instanceof ExcludeClassInterceptors) {
			return true;
		}
		if (object.toString().startsWith("@" + ExcludeClassInterceptors.class.getName()) == true) {
			return true;
		}
		return false;
	}

	private boolean isEnhance(CtClass ctClass) throws ClassNotFoundException, NotFoundException {
		if (ctClass.isInterface() == true) {
			return false;
		}
		Object[] annotations = ctClass.getAnnotations();
		for (int i = 0; i < annotations.length; i++) {
			Object object = annotations[i];
			if (object instanceof Enhance) {
				Enhance enhance = (Enhance) object;
				return enhance.value();
			}
		}
		String packageName = ctClass.getPackageName();
		Package targetPackage = Package.getPackage(packageName);
		if (targetPackage == null) {
			return true;
		}
		Enhance enhance = targetPackage.getAnnotation(Enhance.class);
		if (enhance != null) {
			return enhance.value();
		}

		CtClass superclass = ctClass.getSuperclass();
		if (superclass != null) {
			if (isEnhance(superclass) == false) {
				return false;
			}
		}

		return true;
	}

	private static final String PREFIX_DELEGATE_METHOD = "$$$$$delegateMethod$$$$$";

	private static final String PREFIX_GENERATE_FIELD = "$$$$$generateField$$$$$";

	private void weaveMethod(CtClass ctClass, CtMethod method, String[] interceptorStatements, boolean isDefaultPointcut)
			throws Exception {
		String targetMethodName = method.getName();
		if (targetMethodName.startsWith(PREFIX_DELEGATE_METHOD)) {
			return;
		}

		CtMethod originalMethod = method;
		int modifiers = originalMethod.getModifiers();
		if (Modifier.isAbstract(modifiers)) {
			return;
		}
		String originalMethodName = originalMethod.getName();
		String delegateMethodName = getDelegateMethodPrefix(ctClass) + "_" + originalMethodName;

		String targetClassName = ctClass.getName().replaceAll("\\$", ".");
		CtClass declaringClass = method.getDeclaringClass();
		String declaringClassName = declaringClass.getName().replaceAll("\\$", ".");
		if (targetClassName.equals(declaringClassName) == true) {
			CtMethod delegateMethod = ctClass.getDeclaredMethod(targetMethodName, originalMethod.getParameterTypes());
			delegateMethod.setName(delegateMethodName);
			delegateMethod.setModifiers(originalMethod.getModifiers());
			ctClass.removeMethod(originalMethod);
			ctClass.addMethod(delegateMethod);
		} else {
			if (Modifier.isStatic(originalMethod.getModifiers()) == true) {
				return;
			}
			CtMethod delegateMethod = createCallSuperMethod(originalMethod, ctClass);
			ctClass.addMethod(delegateMethod);
		}

		StringBuilder body = createBody(interceptorStatements, targetMethodName, originalMethod, modifiers, delegateMethodName,
				targetClassName, false, isDefaultPointcut, ctClass);
		CtMethod newMethod = (CtMethod) makeNewBehavior(originalMethod, originalMethodName, body, ctClass, true,
				interceptorStatements);
		ctClass.addMethod(newMethod);
	}

	private StringBuilder createBody(String[] interceptorStatements, String targetName, CtBehavior original, int modifiers,
			String delegateMethodName, String targetClassName, boolean isConstructor, boolean isDefaultPointcut,
			CtClass declaringClass) throws NotFoundException, CannotCompileException {
		CtClass[] parameterTypes = original.getParameterTypes();
		String paramString = getParamString(parameterTypes);
		StringBuilder body = new StringBuilder(1024);
		body.append("jp.dodododo.aop.interceptors.InterceptorChain interceptor = null;\n");
		if (isConstructor == true) {
			body.append("org.aopalliance.intercept.ConstructorInvocation invocation = null;\n");
		} else {
			body.append("org.aopalliance.intercept.MethodInvocation invocation = null;\n");
		}
		body.append("try {\n");

		if (Modifier.isStatic(modifiers) == true) {
			body.append("Object target = null;\n");
		} else {
			body.append("Object target = this;\n");
		}
		body.append("Class targetClass = " + targetClassName + ".class;\n");

		String staticPartFieldName = PREFIX_GENERATE_FIELD + "_staticPart_" + delegateMethodName + "_"
				+ UUID.randomUUID().toString().replaceAll("-", "");
		String delegateMethodFieldName = PREFIX_GENERATE_FIELD + "_" + "_delegateMethod_" + delegateMethodName + "_"
				+ UUID.randomUUID().toString().replaceAll("-", "");

		if (isConstructor == true) {
			String fieldDefSrc = "private static java.lang.reflect.Constructor " + staticPartFieldName
					+ " = jp.dodododo.aop.util.ClassUtil.getDeclaredConstructor(" + targetClassName + ".class," + paramString
					+ ");";
			if (DEBUG) {
				System.out.println(fieldDefSrc);
			}
			declaringClass.addField(CtField.make(fieldDefSrc, declaringClass));
			body.append("java.lang.reflect.Constructor staticPart = " + staticPartFieldName + ";\n");
		} else {
			String fieldDefSrc = "private static java.lang.reflect.Method " + staticPartFieldName
					+ " = jp.dodododo.aop.util.ClassUtil.getDeclaredMethod(" + targetClassName + ".class,\"" + targetName + "\","
					+ paramString + ");";
			if (DEBUG) {
				System.out.println(fieldDefSrc);
			}
			declaringClass.addField(CtField.make(fieldDefSrc, declaringClass));
			body.append("java.lang.reflect.Method staticPart = " + staticPartFieldName + ";\n");
		}

		body.append("if(staticPart == null){throw new jp.dodododo.aop.exception.UnsupportedAopException();}\n");

		body.append("interceptor = new jp.dodododo.aop.interceptors.InterceptorChain(staticPart, " + isDefaultPointcut + ");\n");
		for (int i = 0; i < interceptorStatements.length; i++) {
			body.append("interceptor.add(" + interceptorStatements[i] + ");\n");
		}

		String argsString = getArgsString(original);
		body.append("Object[] args = " + argsString + ";\n");

		String src = "private static final java.lang.reflect.Method " + delegateMethodFieldName
				+ " = jp.dodododo.aop.util.ClassUtil.getDeclaredMethod(" + targetClassName + ".class,\"" + delegateMethodName
				+ "\"," + paramString + ");";
		if (DEBUG) {
			System.out.println(src);
		}
		declaringClass.addField(CtField.make(src, declaringClass));
		body.append("java.lang.reflect.Method delegateMethod = " + delegateMethodFieldName + ";\n");
		body.append("if(delegateMethod == null){throw new jp.dodododo.aop.exception.UnsupportedAopException();}\n");
		body
				.append("invocation = new jp.dodododo.aop.intercept.DelegateMethodInvocation(delegateMethod, target, targetClass, staticPart, args);\n");

		CtClass returnType = null;
		if (original instanceof CtMethod) {
			returnType = ((CtMethod) original).getReturnType();
		}

		body.append("} catch (jp.dodododo.aop.exception.UnsupportedAopException e) {\n");
		appendInvokeOriginal(body, delegateMethodName, parameterTypes, returnType);
		body.append("} catch (NoClassDefFoundError e) {\n");
		appendInvokeOriginal(body, delegateMethodName, parameterTypes, returnType);
		body.append("}\n");

		if (isConstructor) {
			body.append("interceptor.construct(invocation);\n");
			return body;
		}

		if (isReturnVoid(returnType) == true) {
			body.append("interceptor.invoke(invocation)");
		} else {
			if (returnType.isPrimitive() == false) {
				String returnTypeString = returnType.getName();
				body.append("return (" + returnTypeString + ")"); // cast
				body.append("interceptor.invoke(invocation)");
			} else {
				CtPrimitiveType primitiveType = (CtPrimitiveType) returnType;
				String returnTypeString = primitiveType.getWrapperName();
				body.append("return ((" + returnTypeString + ")"); // cast
				body.append("interceptor.invoke(invocation)).");
				body.append(primitiveType.getGetMethodName()); // andToPrimitive
				body.append("()");
			}
		}
		body.append(";\n");
		return body;
	}

	private void appendInvokeOriginal(StringBuilder body, String delegateMethodName, CtClass[] parameterTypes, CtClass returnType) {
		if (isReturnVoid(returnType) == false) {
			body.append("return ");
		}
		body.append(delegateMethodName + "(  ");
		for (int i = 0; i < parameterTypes.length; i++) {
			body.append(" " + "$" + (i + 1) + ", ");
		}
		body.setLength(body.length() - 2);
		body.append(");\n");
		if (isReturnVoid(returnType) == true) {
			body.append("return;\n");
		}
	}

	private CtMethod createCallSuperMethod(CtMethod originalMethod, CtClass declaringClass) throws Exception {
		CtClass returnType = originalMethod.getReturnType();
		CtClass[] parameters = originalMethod.getParameterTypes();

		StringBuilder body = new StringBuilder();
		if (isReturnVoid(returnType) == false) {
			body.append("return ");
		}
		body.append("super.");
		body.append(originalMethod.getName());
		body.append("(  ");
		for (int i = 0; i < parameters.length; i++) {
			body.append("$" + (i + 1) + " , ");
		}
		body.setLength(body.length() - 2);
		body.append(")");
		body.append(";");
		return (CtMethod) makeNewBehavior(originalMethod, getDelegateMethodPrefix(declaringClass) + "_"
				+ originalMethod.getName(), body, declaringClass, false, null);
	}

	private boolean isReturnVoid(CtClass returnType) {
		if (returnType == null) {
			return true;
		}
		return returnType.getName().equals("void");
	}

	private String getArgsString(CtBehavior behavior) throws NotFoundException {
		CtClass[] parameterTypes = behavior.getParameterTypes();
		if (parameterTypes.length == 0) {
			return "null";
		}
		StringBuilder ret = new StringBuilder(128);
		ret.append("new Object[]{  ");
		for (int i = 0; i < parameterTypes.length; i++) {
			CtClass ctClass = parameterTypes[i];
			if (ctClass.isPrimitive()) {
				CtPrimitiveType primitiveType = (CtPrimitiveType) ctClass;
				ret.append(primitiveType.getWrapperName());
				ret.append(".valueOf(").append("$" + (i + 1)).append(")");
			} else {
				ret.append("$" + (i + 1));
			}
			ret.append(", ");
		}
		ret.setLength(ret.length() - 2);
		ret.append("}");
		return ret.toString();
	}

	private String getParamString(CtClass[] parameterTypes) throws NotFoundException {
		if (parameterTypes.length == 0) {
			return "null";
		}
		StringBuilder str = new StringBuilder(128);
		str.append("new Class[]{  ");
		for (CtClass ctClass : parameterTypes) {
			if (ctClass.isArray() == true) {
				String componentClassName = getComponentClassName(ctClass);
				int size = getSize(ctClass, 0);
				str.append("jp.dodododo.aop.util.ClassUtil.getArrayClass(");

				str.append(componentClassName);
				str.append(".class");
				str.append(",");
				str.append(size);
				str.append(")");
			} else {
				str.append(ctClass.getName());
				str.append(".class");
			}
			str.append(", ");
		}
		str.setLength(str.length() - 2);
		str.append("}");
		return str.toString();
	}

	private int getSize(CtClass ctClass, int size) throws NotFoundException {
		if (ctClass.isArray()) {
			CtClass componentType = ctClass.getComponentType();
			return getSize(componentType, (size + 1));
		}
		return size;
	}

	private String getComponentClassName(CtClass ctClass) throws NotFoundException {
		if (ctClass.isArray() == true) {
			return getComponentClassName(ctClass.getComponentType());
		}
		return ctClass.getName();
	}

	private CtBehavior makeNewBehavior(CtBehavior template, String name, CharSequence body, CtClass declaringClass,
			boolean isTryCatch, String[] interceptorStatements) throws Exception {
		int modifiers = template.getModifiers();
		if (Modifier.isNative(modifiers) == true) {
			modifiers = modifiers - Modifier.NATIVE;
		}

		CtClass returnType = null;
		CtClass[] exceptionTypes = null;
		if (template instanceof CtMethod) {
			returnType = ((CtMethod) template).getReturnType();
			exceptionTypes = ((CtMethod) template).getExceptionTypes();
		}
		if (name == null) {
			name = template.getName();
		}
		if (name.contains(PREFIX_DELEGATE_METHOD) == true) {
			modifiers = toPrivate(modifiers);
		}
		CtClass[] parameters = template.getParameterTypes();
		CtClass[] exceptions = template.getExceptionTypes();
		if (declaringClass == null) {
			declaringClass = template.getDeclaringClass();
		}
		StringBuilder src = new StringBuilder(1024);

		src.append(getAccessModifier(modifiers) + " ");
		if (Modifier.isStatic(modifiers)) {
			src.append("static ");
		}
		if (returnType != null) {
			src.append(returnType.getName() + " ");
		}
		src.append(name + "(  ");
		for (int i = 0; i < parameters.length; i++) {
			CtClass param = parameters[i];
			src.append("  " + param.getName() + "  $" + (i + 1) + " , ");
		}
		src.setLength(src.length() - 2);
		src.append(") {\n");
		if (isTryCatch == true) {
			src.append("try {\n");
		}
		src.append(body);
		if (isTryCatch == true) {
			src.append("} catch (java.lang.reflect.InvocationTargetException e) {\n");
			src.append("throw (e.getCause()!=null? e.getCause() : new RuntimeException(e));\n");
			for (int i = 0; i < exceptionTypes.length; i++) {
				CtClass ctClass = exceptionTypes[i];
				String exceptionName = ctClass.getName();
				if (Error.class.getName().equals(exceptionName) || RuntimeException.class.getName().equals(exceptionName)) {
					continue;
				}
				src.append("} catch (" + exceptionName + " e) {throw e;\n");
			}
			src.append("} catch (Error e) {throw e;\n");
			src.append("} catch (RuntimeException e) {throw e;\n");
			src.append("}\n");
		}
		src.append("}\n");

		if (DEBUG == true) {
			System.out.println(src);
		}

		CtBehavior newBehavior = null;
		try {
			if (returnType != null) {
				newBehavior = CtNewMethod.make(src.toString(), declaringClass);
			} else {
				newBehavior = CtNewConstructor.make(src.toString(), declaringClass);
			}
		} catch (Exception e) {
			if (interceptorStatements != null && 0 < interceptorStatements.length) {
				Thread.sleep(10);
				for (int i = 0; i < interceptorStatements.length; i++) {
					System.err.println(interceptorStatements[i]);
				}
				Thread.sleep(10);
			}
			throw e;
		}
		newBehavior.setExceptionTypes(exceptions);
		newBehavior.setModifiers(modifiers);
		copyMethodAttributes(template, newBehavior);
		return newBehavior;
	}

	private int toPrivate(int modifiers) {
		if (Modifier.isPrivate(modifiers)) {
			return modifiers;
		} else if (Modifier.isPublic(modifiers)) {
			return modifiers - Modifier.PUBLIC + Modifier.PRIVATE;
		} else if (Modifier.isProtected(modifiers)) {
			return modifiers - Modifier.PROTECTED + Modifier.PRIVATE;
		} else {
			return modifiers + Modifier.PRIVATE;
		}
	}

	/**
	 * copy annotation
	 * 
	 * @param src
	 * @param dest
	 */
	private void copyMethodAttributes(CtBehavior src, CtBehavior dest) {
		MethodInfo srcMethodInfo;
		try {
			srcMethodInfo = src.getMethodInfo();
		} catch (RuntimeException ignore) {
			return;
		}
		@SuppressWarnings("unchecked")
		List<AttributeInfo> attributes = srcMethodInfo.getAttributes();
		MethodInfo methodInfo = dest.getMethodInfo();
		for (Iterator<AttributeInfo> iter = attributes.iterator(); iter.hasNext();) {
			AttributeInfo info = iter.next();
			if (info instanceof AnnotationDefaultAttribute || info instanceof AnnotationsAttribute) {
				methodInfo.addAttribute(info);
			}
		}
	}

	private static String getAccessModifier(int modifiers) {
		if (Modifier.isPublic(modifiers)) {
			return "public";
		} else if (Modifier.isProtected(modifiers)) {
			return "protected";
		} else if (Modifier.isPrivate(modifiers)) {
			return "private";
		} else {
			return "";
		}
	}

	private String getDelegateMethodPrefix(CtClass ctClass) {
		StringBuilder ret = new StringBuilder(PREFIX_DELEGATE_METHOD);
		ret.append(ctClass.getName().replaceAll("\\.", "_").replaceAll("\\/", "_"));
		return ret.toString();
	}
}
